#include "CsbufParser.h"
#include <cstring>

static int x = 1;
static bool isLittleEndian1 = *(char*)&x == 1;


CsbufParser::CsbufParser()
: _cursor(0)
{
}


CsbufParser::CsbufParser(const CsbufParser& buf)
: _cursor(0)
{
	this->_data = buf._data;
}

CsbufParser::~CsbufParser()
{
}

void CsbufParser::setData(const CsbufParser& buf)
{
	_data.assign(buf._data);
}

void CsbufParser::setData(const std::string& data)
{
	_data.assign(data);
}

void CsbufParser::setData(const char* data, unsigned int len)
{
	_data.assign(data, len);
	_cursor = 0;
}

void CsbufParser::clear()
{
	_data.clear();
	_cursor = 0;
	while (!_posStack.empty())	_posStack.pop();
}

void CsbufParser::setBool(bool d, uint8_t pos)
{
	if (!d && 0 != pos)	return;

	_data.push_back(pos);
	_data.push_back(buftype_bool);
	_data.push_back(d);
}

void CsbufParser::setUint8(uint8_t d, uint8_t pos)
{
	if (d == 0 && 0 != pos)	return;

	_data.push_back(pos);
	_data.push_back(buftype_uint8);
	_data.push_back(d);
}

void CsbufParser::setUint16(uint16_t d, uint8_t pos)
{
	if (d == 0 && 0 != pos)	return;

	_data.push_back(pos);
	_data.push_back(buftype_uint16);
	_append((const void*)&d, 2);
}

void CsbufParser::setUint32(uint32_t d, uint8_t pos)
{
	if (d == 0 && 0 != pos)	return;


	if (d <= 255)
	{
		setUint8(uint8_t(d), pos);
	}
	else if (d <= 65535)
	{
		setUint16(uint16_t(d), pos);
	}
	else
	{
		_data.push_back(pos);
		_data.push_back(buftype_uint32);
		_append((const char*)&d, 4);
	}
}

void CsbufParser::setUint64(uint64_t d, uint8_t pos)
{
	if (d == 0 && 0 != pos)	return;

	_data.push_back(pos);
	_data.push_back(buftype_uint64);
	_append((const char*)&d, 8);
}

void CsbufParser::setInt8(int8_t d, uint8_t pos)
{
	if (d == 0 && 0 != pos)	return;

	_data.push_back(pos);
	_data.push_back(buftype_int8);
	_data.push_back(d);
}

void CsbufParser::setInt16(int16_t d, uint8_t pos)
{
	if (d == 0 && 0 != pos)	return;

	_data.push_back(pos);
	_data.push_back(buftype_int16);
	_append((const char*)&d, 2);
}

void CsbufParser::setInt32(int32_t d, uint8_t pos)
{
	if (d == 0 && 0 != pos)	return;

	if (d >= -128 && d <= 127)
	{
		setInt8(int8_t(d), pos);
	}
	else if (d >= -32768 && d <= 32767)
	{
		setInt16(int16_t(d), pos);
	}
	else
	{
		_data.push_back(pos);
		_data.push_back(buftype_int32);
		_append((const char*)&d, 4);
	}
}

void CsbufParser::setInt64(int64_t d, uint8_t pos)
{
	if (d == 0 && 0 != pos)	return;

	_data.push_back(pos);
	_data.push_back(buftype_int64);
	_append((const char*)&d, 8);
}

void CsbufParser::setFloat(float d, uint8_t pos)
{
	if (d > -0.00000001f && d < 0.00000001f && 0 != pos)	return;

	_data.push_back(pos);
	_data.push_back(buftype_float);
	_append((const char*)&d, 4);
}

void CsbufParser::setDouble(double d, uint8_t pos)
{
	if (d > -0.00000001 && d < 0.00000001 && 0 != pos)	return;

	_data.push_back(pos);
	_data.push_back(buftype_double);
	_append((const char*)&d, 8);
}

void CsbufParser::setString(const char* d, uint8_t pos)
{
	setString(d, strlen(d), pos);
}

void CsbufParser::setString(const char* d, size_t n, uint8_t pos)
{
	if (n == 0 && 0 != pos)	return;

	_data.push_back(pos);

	if (n <= 255)
	{
		_data.push_back(buftype_string_s);
		_append((const char*)&n, 1);
	}
	else if (n <= 65535)
	{
		_data.push_back(buftype_string);
		_append((const char*)&n, 2);
	}
	else
	{
		_data.push_back(buftype_string_l);
		_append((const char*)&n, 4);
	}
	_data.append(d, n);
}

void CsbufParser::setString(const std::string& d, uint8_t pos)
{
	setString(d.c_str(), d.size(), pos);
}

void CsbufParser::setArray(uint16_t d, uint8_t pos)
{
	if (d == 0 && 0 != pos)	return;

	if (d <= 255)
		setUint8(d, pos);
	else
		setInt16(d, pos);
}

void CsbufParser::setMessage(uint8_t pos)
{
	_data.push_back(pos);
	_data.push_back(buftype_string);
	_data.push_back(0);
	_data.push_back(0);
	_posStack.push(_data.size());
}

void CsbufParser::endSetMessage()
{
	if (!_posStack.empty())
	{
		size_t pos = (size_t)_posStack.top();
		_posStack.pop();
		size_t len = _data.size() - pos;
		if (len > 0)
		{
			const char* ch = (const char*)&len;
			if (len <= 65535)
			{
				if (isLittleEndian1)
				{
					_data[pos - 2] = (*(ch + 1));
					_data[pos - 1] = (*ch);
				}
				else
				{
					_data[pos - 1] = (*(ch + 1));
					_data[pos - 2] = (*ch);
				}
			}
			else
			{
				_data.push_back(0);
				_data.push_back(0);
				for (size_t i = _data.size() - 3; i >= pos; --i)
				{
					_data[i + 2] = _data[i];
				}
				if (isLittleEndian1)
				{
					_data[pos - 2] = (*(ch + 3));
					_data[pos - 1] = (*(ch + 2));
					_data[pos] = (*(ch + 1));
					_data[pos + 1] = (*(ch));
				}
				else
				{
					_data[pos - 2] = (*(ch));
					_data[pos - 1] = (*(ch + 1));
					_data[pos] = (*(ch + 2));
					_data[pos + 1] = (*(ch + 3));
				}
				_data[pos - 3] = buftype_string_l;
			}
		}
		else if (_data[pos - 4] != 0)	// 在数组内时不节省发送数据
		{
			_data.erase(pos - 4, 4);
		}
	}
}

bool CsbufParser::getBool(uint8_t pos)
{
	bool ret = false;
	_setValue(&ret, 1, pos);
	return ret;
}

uint8_t CsbufParser::getUint8(uint8_t pos)
{
	uint8_t ret = 0;
	_setValue(&ret, 1, pos);
	return ret;
}

uint16_t CsbufParser::getUint16(uint8_t pos)
{
	uint16_t ret = 0;
	_setValue(&ret, 2, pos);
	return ret;
}

uint32_t CsbufParser::getUint32(uint8_t pos)
{
	if (_setCursorWithPos(pos)) {
		size_t s = _getSize();
		if (_cursor + 1 < s)
		{
			auto n = _data[_cursor + 1];
			if (1 == n)
			{
				uint8_t ret = 0;
				_setValue(&ret, n, pos);
				return uint32_t(ret);
			}
			else if (2 == n)
			{
				uint16_t ret = 0;
				_setValue(&ret, n, pos);
				return uint32_t(ret);
			}
			else if (4 == n)
			{
				uint32_t ret = 0;
				_setValue(&ret, n, pos);
				return uint32_t(ret);
			}
		}
	}
	return 0;

	//uint32_t ret = 0;
	//_setValue(&ret, 4, pos);
	//return ret;
}

uint64_t CsbufParser::getUint64(uint8_t pos)
{
	uint64_t ret = 0;
	_setValue(&ret, 8, pos);
	return ret;
}

int8_t CsbufParser::getInt8(uint8_t pos)
{
	int8_t ret = 0;
	_setValue(&ret, 1, pos);
	return ret;
}

int16_t CsbufParser::getInt16(uint8_t pos)
{
	int16_t ret = 0;
	_setValue(&ret, 2, pos);
	return ret;
}

int32_t CsbufParser::getInt32(uint8_t pos)
{
	if (_setCursorWithPos(pos)) {
		size_t s = _getSize();
		if (_cursor + 1 < s)
		{
			auto n = _data[_cursor + 1];
			if (1 == n)
			{
				int8_t ret = 0;
				_setValue(&ret, n, pos);
				return int32_t(ret);
			}
			else if (2 == n)
			{
				int16_t ret = 0;
				_setValue(&ret, n, pos);
				return int32_t(ret);
			}
			else if (4 == n)
			{
				int32_t ret = 0;
				_setValue(&ret, n, pos);
				return int32_t(ret);
			}
		}
	}
	return 0;

	//int32_t ret = 0;
	//_setValue(&ret, 4, pos);
	//return ret;
}

int64_t CsbufParser::getInt64(uint8_t pos)
{
	int64_t ret = 0;
	_setValue(&ret, 8, pos);
	return ret;
}

float CsbufParser::getFloat(uint8_t pos)
{
	float ret = 0.0f;
	_setValue(&ret, 4, pos);
	return ret;
}

double CsbufParser::getDouble(uint8_t pos)
{
	double ret = 0.0;
	_setValue(&ret, 8, pos);
	return ret;
}

std::string CsbufParser::getString(uint8_t pos)
{
	std::string ret;
	if (_setCursorWithPos(pos))
	{
		size_t s = _getSize();
		if (_cursor + 3 < s && (uint8_t)_data[_cursor + 1] == buftype_string)
		{
			_cursor += 2;
			uint16_t len = 0;
			_memcpy(&len, _data.c_str() + _cursor, 2, isLittleEndian1);
			_cursor += 2;
			if (_cursor + len <= s)
			{
				ret.assign(_data.c_str() + _cursor, len);
			}
			_cursor += len;
		}
		else if (_cursor + 2 < s && (uint8_t)_data[_cursor + 1] == buftype_string_s)
		{
			_cursor += 2;
			uint8_t len = 0;
			_memcpy(&len, _data.c_str() + _cursor, 1, isLittleEndian1);
			_cursor += 1;
			if (_cursor + len <= s)
			{
				ret.assign(_data.c_str() + _cursor, len);
			}
			_cursor += len;
		}
		else if (_cursor + 5 < s && (uint8_t)_data[_cursor + 1] == buftype_string_l)
		{
			_cursor += 2;
			uint32_t len = 0;
			_memcpy(&len, _data.c_str() + _cursor, 4, isLittleEndian1);
			_cursor += 4;
			if (_cursor + len <= s)
			{
				ret.assign(_data.c_str() + _cursor, len);
			}
			_cursor += len;
		}
	}
	return ret;
}

uint16_t CsbufParser::getArray(uint8_t pos)
{
	if (!_setCursorWithPos(pos))	return 0;

	uint16_t ret = 0;
	size_t s = _getSize();
	if (_cursor + 1 < s)
	{
		if (_data[_cursor + 1] == 1)
		{
			if (_cursor + 2 < s)
				ret = (uint8_t)_data[_cursor + 2];
			_cursor += 3;
		}
		else if (_data[_cursor + 1] == 2 && _cursor + 3 < s)
		{
			_memcpy(&ret, _data.c_str() + (2 + _cursor), 2, isLittleEndian1);
			_cursor += 4;
		}
	}
	return ret;
}

uint16_t CsbufParser::getMessage(uint8_t pos)
{
	uint16_t ret = 0;
	if (_setCursorWithPos(pos))
	{
		size_t s = _getSize();
		if (_cursor + 3 < s && (uint8_t)_data[_cursor + 1] == buftype_string)
		{
			_cursor += 2;
			_memcpy(&ret, _data.c_str() + _cursor, 2, isLittleEndian1);
			_cursor += 2;
			_posStack.push(_cursor + ret);
		}
		else if (_cursor + 5 < s && (uint8_t)_data[_cursor + 1] == buftype_string_l)
		{
			_cursor += 2;
			_memcpy(&ret, _data.c_str() + _cursor, 4, isLittleEndian1);
			_cursor += 4;
			_posStack.push(_cursor + ret);
		}
		else
		{
			_posStack.push(_cursor);
		}
	}
	else
	{
		_posStack.push(_cursor);
	}
	return ret;
}

void CsbufParser::endGetMessage()
{
	if (!_posStack.empty())
	{
		_cursor = _posStack.top();
		_posStack.pop();
	}
}

bool CsbufParser::_setCursorWithPos(uint8_t pos)
{
	size_t s = _getSize();


	if (_cursor + 1 < s)
	{
		uint8_t p = _data[_cursor];
		if (p == pos)
		{
			return true;
		}
		else if (p < pos)
		{
			if (buftype_string == (uint8_t)_data[_cursor + 1])
			{
				_cursor += 2;
				if (_cursor + 2 >= s)	return false;
				uint16_t len = 0;
				_memcpy(&len, _data.c_str() + _cursor, 2, isLittleEndian1);
				_cursor += 2 + len;
			}
			else if (buftype_string_s == (uint8_t)_data[_cursor + 1])
			{
				_cursor += 2;
				if (_cursor + 2 >= s)	return false;
				uint8_t len = 0;
				_memcpy(&len, _data.c_str() + _cursor, 1, isLittleEndian1);
				_cursor += 1 + len;
			}
			else
				_cursor += 2 + _data[_cursor + 1];
			return _setCursorWithPos(pos);
		}
	}

	return false;
}

bool CsbufParser::_setValue(void* dest, size_t n, uint8_t pos)
{
	if (!_setCursorWithPos(pos))	return false;

	size_t s = _getSize();
	if (_cursor + 1 + n < s)
	{
		//++_cursor;
		if (_data[_cursor + 1] == (uint8_t)n)
		{
			//++_cursor;

			//if (_cursor + 2 + n <= s)
			{
				_memcpy(dest, _data.c_str() + (2 + _cursor), n, isLittleEndian1);
			}
			_cursor += 2 + n;
			return true;
		}
	}
	return false;
}

bool CsbufParser::_setValue2(void* dest, size_t n, uint8_t pos)
{
	size_t s = _getSize();
	if (_cursor + 1 + n < s)
	{
		//++_cursor;
		if (_data[_cursor + 1] == (uint8_t)n)
		{
			//++_cursor;

			//if (_cursor + 2 + n <= s)
			{
				_memcpy(dest, _data.c_str() + (2 + _cursor), n, isLittleEndian1);
			}
			_cursor += 2 + n;
			return true;
		}
	}
	return false;
}

void CsbufParser::_memcpy(void* dest, const void* src, size_t n, bool isEndian)
{
	if (isEndian && n > 1)
	{
		for (size_t i = 0; i < n; ++i)
		{
			*((uint8_t*)dest + i) = *((uint8_t*)src + (n - 1 - i));
		}
	}
	else
	{
		std::memcpy(dest, src, n);
	}
}

void CsbufParser::_append(const void* src, size_t n)
{
	if (isLittleEndian1)
	{
		const char* ch = (const char*)src;
		for (size_t i = 0; i < n; ++i)
		{
			auto j = n - i - 1;
			_data.push_back(*(ch + n - i - 1));
		}
	}
	else
	{
		_data.append((const char*)&src, n);
	}
}

size_t CsbufParser::_getSize()
{
	if (!_posStack.empty())
	{
		return (size_t)_posStack.top();
	}
	return _data.size();
}
